﻿#region Header
// ------------------------ Licence / Copyright ------------------------
// 
// ScrumTable for Scrum Meeting Support
// Copyright © HSR - Hochschule für Technik Rapperswil
// All Rights Reserved
// 
// Author:
//  Michael Gfeller Silvan Gehrig Patrick Boos
// 
// ---------------------------------------------------------------------
#endregion

#region Usings

using System;
using System.Collections.Generic;
using System.Linq;
using System.Xml.Serialization;
using ScrumTable.BL.DM.DataManagement;
using ScrumTable.Common.Logging;
using ScrumTable.Common.Security;
using ScrumTable.Common.Threading;

#endregion

namespace ScrumTable.Config.UserManagement
{

    /// <summary>
    /// Represents a LoginAccountManager
    /// </summary>
    [XmlRoot(ElementName = "Root")]
    public class LoginAccountManager : ILoginAccountManager, ISerializationMarker<LoginAccountManager>
    {
        #region Declarations
        //--------------------------------------------------------------------
        // Declarations
        //--------------------------------------------------------------------

        private readonly List<LoginAccount> _userList = new List<LoginAccount>();

        /// <summary>
        /// Gets or Sets the LicenseKey
        /// </summary>
        [XmlAttribute]
        public string LicenseKey { get; set; }

        /// <summary>
        /// Gets or Sets the LicenseKey
        /// </summary>
        [XmlAttribute]
        public string EncryptedKey { get; set; }

        /// <summary>
        /// If set of false, no further checks for a new version are mad
        /// </summary>
        [XmlAttribute]
        public bool DisableCheckForUpates { get; set; }
 
        /// <summary>
        /// Checks if this account is activated 
        /// </summary>
        [XmlIgnore]
        public bool IsActivated 
        {
            get
            {
                return true;
            }
        }

         
        #endregion

        #region Properties
        //--------------------------------------------------------------------
        // Properties
        //--------------------------------------------------------------------

        /// <summary>
        /// Gets or sets the user list.
        /// </summary>
        [XmlArray("UserList")]
        [XmlArrayItem(Type = typeof(LoginAccount), ElementName = "Account")]
        public List<LoginAccount> UserList
        {
            get { return _userList; }
        }


        /// <summary>
        /// Gets a tfs-server list
        /// </summary>
        /// <value>The name of the server.</value>
        [XmlArray("ServerList")]
        public List<string> ServerList { get; set; }

        /// <summary>
        /// Gets or sets the current login account.
        /// </summary>
        /// <value>The current login account.</value>
        [XmlIgnore]
        public LoginAccount CurrentLoginAccount { get; set; }

        /// <summary>
        /// Contains the serializer
        /// </summary>
        [XmlIgnore]
        public ConfigSerializer<LoginAccountManager> Serializer { get; set; }

        #endregion

        #region Constructors / Destructor

        //--------------------------------------------------------------------
        // Constructors / Destructor
        //--------------------------------------------------------------------
        /// <summary>
        /// DO NOT USE THIS CONSTRUCTOR FROM GLOBAL CODE. THE SERIALIZER NEEDS
        /// A PUBLIC CONSTRUCTOR!
        /// </summary>
        public LoginAccountManager()
        {
            ServerList = new List<string>();            
            CurrentLoginAccount = new LoginAccount();
        }

        #endregion

        #region Methods
        //--------------------------------------------------------------------
        // Methods
        //--------------------------------------------------------------------

        /// <summary>
        /// Determines whether [contains login account] [the specified identity tag value].
        /// </summary>
        /// <param name="identityTagValue">The identity tag value.</param>
        /// <returns>
        /// 	<c>true</c> if [contains login account] [the specified identity tag value]; otherwise, <c>false</c>.
        /// </returns>
        public bool ContainsLoginAccount(long identityTagValue)
        {
            return (GetLoginAccount(identityTagValue) != null);
        }

        /// <summary>
        /// Gets the login account.
        /// </summary>
        /// <param name="identityTagValue">The identity tag value.</param>
        /// <returns></returns>
        public LoginAccount GetLoginAccount(long identityTagValue)
        {
            return (from x in UserList
                   where x.IdentityTagValue == identityTagValue
                   select x).FirstOrDefault();
        }

        /// <summary>
        /// Adds the user.
        /// </summary>
        /// <param name="identityTagValue">The account identity.</param>
        /// <returns></returns>
        public LoginAccount AddUser(long identityTagValue)
        {
            LoginAccount account;
            if (!ContainsLoginAccount(identityTagValue))
            {
                account = new LoginAccount { IdentityTagValue = identityTagValue };
                UserList.Add(account);
            }
            else
            {
                account = GetLoginAccount(identityTagValue);
            }
            return account;
        }

        /// <summary>
        /// Sets the current login account.    
        /// If the id does not exist a new account will be created    
        /// </summary>
        /// <param name="identityTagValue">Login account id</param>
        public void SetCurrentLoginAccount(long identityTagValue)
        {
            LoginAccount loginAccount = GetLoginAccount(identityTagValue);
            if( loginAccount == null)
            {
                loginAccount = AddUser(identityTagValue);
            }

            CurrentLoginAccount = loginAccount;
        }

        /// <summary>
        /// Adds a new LoginAccount and sets it as current LoginAccount
        /// </summary>
        public void AddAndSetLoginAccount()
        {
            CurrentLoginAccount = AddUser(GetFreeLoginId());
        }



        /// <summary>
        /// Saves the user list.
        /// </summary>
        public void SaveLogin()
        {
            CleanUpUserList();
            Serializer.Serialize(this);
        }


        /// <summary>
        /// Saves the user list.
        /// </summary>
        /// <param name="path"></param>
        public void SaveLogin(string path)
        {
            CleanUpUserList();
            new ConfigSerializer<LoginAccountManager>(path, path).Serialize(this);
        }



        /// <summary>
        /// Connects the specified context.
        /// </summary>
        /// <param name="dispatcher">Specifies the sychronization dispatcher for the async callback.</param>
        /// <param name="pin"></param>
        /// <param name="context">The context.</param>
        /// <param name="func">Action which is called after the connection process has been completed.</param>
        public void Connect(IDataSyncDispatcher dispatcher, string pin, IDomainContext context, Action<bool, string> func)
        {
            if (!CurrentLoginAccount.CanLogin)
            {
                throw new LoginAccountConnectException(CurrentLoginAccount.IdentityTagValue.ToString());
            }
            
            CurrentLoginAccount.Decrypt(pin);

            var pwds = CurrentLoginAccount.DeCryptedLogins;
            ConnectionInformation[] connectionInformations = new ConnectionInformation[pwds.Count];
            
            for (int index = 0; index < pwds.Count; index++)
            {
                var serializablePair = pwds[index];
                connectionInformations[index] = new ConnectionInformation(
                    serializablePair.Left,
                    serializablePair.Right);
            }

            pwds = null;

            context.Connect(
                dispatcher,
                new ConnectionInformationManager( connectionInformations), 
                r =>
                    {
                        Exception error = ((IDataResult)r).Error;

                        if (error != null)
                            Logger<LoginAccountManager>.Error(error);

                        func(error == null,(error==null)?"":error.Message);
                    }, CurrentLoginAccount.ServerName);
            
        }

        private long GetFreeLoginId()
        {
            UserList.Sort((a, b) => a.IdentityTagValue.CompareTo(b.IdentityTagValue));
            long id = 0;
            foreach (var user in UserList)
            {
                if (user.IdentityTagValue == id)
                {
                    id++;
                }
                else
                {
                    break;
                }
            }
            return id;
        }

        private void CleanUpUserList()
        {
            IEnumerable<LoginAccount> list = new List<LoginAccount>(UserList);
            foreach (LoginAccount account in list)
            {
                if (!account.CanLogin)
                {
                    UserList.Remove(account);
                }
            }
        }


        /// <summary>
        /// Adds a member to the list of hidden members
        /// </summary>
        /// <param name="displayedName">displayedname of the user</param>
        public void AddHiddenMember(string displayedName)
        {
            if (!CurrentLoginAccount.HiddenMembers.Contains(displayedName))
            {
                CurrentLoginAccount.HiddenMembers.Add(displayedName);
            }
        }

        /// <summary>
        /// Removes a member from the list of hidden members
        /// </summary>
        /// <param name="displayedName">displayedname of the user</param>
        public void RemoveHiddenMember(string displayedName)
        {
            if (CurrentLoginAccount.HiddenMembers.Contains(displayedName))
            {
                CurrentLoginAccount.HiddenMembers.Remove(displayedName);
            }
        }

        /// <summary>
        /// Determines whether the member is hidden
        /// </summary>
        /// <param name="displayedName">displayedname of the user.</param>
        /// <returns>
        /// 	<c>true</c> if the member is hidden; otherwise, <c>false</c>.
        /// </returns>
        public bool ContainsHiddenMember(string displayedName)
        {
            return CurrentLoginAccount.HiddenMembers.Contains(displayedName);
        }

        #endregion

        #region Events
        //--------------------------------------------------------------------
        // Events
        //--------------------------------------------------------------------

        #endregion
    }
}
